package com.briup.common.utils;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.briup.common.exception.ServiceException;
import com.briup.common.response.ResultCode;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 工具类，用于各个实体类型对象之间的转换，
 * 包括将一个实体对象转换为另一个实体对象的操作
 */
public class BeanUtil {

    /**
     * 将分页对象中的记录转换为目标类型的分页对象
     *
     * @param sourcePage  源分页对象
     * @param targetClass 目标类型
     * @return 目标类型的分页对象
     */
    public static <S, T> Page<T> convertPage(Page<S> sourcePage, Class<T> targetClass) {
        // 创建目标分页对象
        Page<T> targetPage = new Page<>(sourcePage.getCurrent(), sourcePage.getSize());
        // 转换记录列表
        List<T> targetRecords = sourcePage.getRecords().stream()
                .map(source -> convert(source, targetClass))
                .toList();
        // 设置目标分页对象的记录列表
        targetPage.setRecords(targetRecords
                .stream().skip((sourcePage.getCurrent() - 1) * sourcePage.getSize())
                .limit(sourcePage.getSize())
                .collect(Collectors.toList()));
        // 设置总记录数
        targetPage.setTotal(sourcePage.getRecords().size());
        return targetPage;
    }

    /**
     * 将单个资源对象转换为目标类型对象
     *
     * @param source      当前资源对象
     * @param targetClass 目标类型
     * @return 目标类型的对象
     */
    public static <S, T> T convert(S source, Class<T> targetClass) {
        try {
            // 先创建目标类型的对象
            Constructor<T> constructor = targetClass.getDeclaredConstructor();
            T t = constructor.newInstance();
            // 借助于Spring中的BeanUtils类中的copyProperties()方法将资源对象中的数据拷贝到目标类型对象中
            BeanUtils.copyProperties(source, t);
            // 返回装载了数据的目标类型对象
            return t;
        } catch (Exception e) {
            throw new ServiceException(ResultCode.BEAN_CONVERT_ERROR);
        }
    }

    /**
     * 将单个资源对象转换为目标类型对象，忽略指定的属性
     *
     * @param source           当前资源对象
     * @param targetClass      目标类型
     * @param ignoreProperties 要忽略的属性名数组
     * @return 目标类型的对象
     */
    public static <S, T> T convert(S source, Class<T> targetClass, String... ignoreProperties) {
        try {
            // 先创建目标类型的对象
            Constructor<T> constructor = targetClass.getDeclaredConstructor();
            T t = constructor.newInstance();
            BeanUtils.copyProperties(source, t, ignoreProperties);
            // 返回装载了数据的目标类型对象
            return t;
        } catch (Exception e) {
            throw new ServiceException(ResultCode.BEAN_CONVERT_ERROR);
        }
    }

    /**
     * 将资源对象列表转换为目标类型对象列表
     *
     * @param sourceList  当前资源对象列表
     * @param targetClass 目标类型
     * @return 目标类型对象列表
     */
    public static <S, T> List<T> convertList(List<S> sourceList, Class<T> targetClass) {
        try {
            return sourceList.stream()
                    .map(source -> {
                        try {
                            return convert(source, targetClass);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }).toList();
        } catch (Exception e) {
            throw new ServiceException(ResultCode.BEAN_CONVERT_ERROR);
        }
    }

    /**
     * 将资源对象列表转换为目标类型对象列表，忽略指定的属性
     *
     * @param sourceList       当前资源对象列表
     * @param targetClass      目标类型
     * @param ignoreProperties 要忽略的属性名数组
     * @return 目标类型对象列表
     */
    public static <S, T> List<T> convertList(List<S> sourceList, Class<T> targetClass, String... ignoreProperties) {
        try {
            return sourceList.stream()
                    .map(source -> {
                        try {
                            return convert(source, targetClass, ignoreProperties);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }).toList();
        } catch (Exception e) {
            throw new ServiceException(ResultCode.BEAN_CONVERT_ERROR);
        }
    }
}
